/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package JET.mygui;

import JET.mygui.interfaces.MyGuiDnDReady;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Point;
import javax.swing.JComponent;
import javax.swing.JScrollPane;
import JET.mygui.CenterTabsPane.CenterTabsPaneTab;
import JET.mygui.UtilTabsPane.UtilTabsPaneTab;
import java.awt.event.FocusListener;
import java.util.ArrayList;


/**
 *
 * @author raos
 */
public class MyGuiHelper {

    /**
     *
     * @param comp
     * @return
     */
    public static JScrollPane getNewJScrollPane(Component comp) {
        JScrollPane sp = new JScrollPane(comp);
        sp.setBorder(null);
        return sp;
    }
    /**
     * Calculates length of a string in given graphics context.
     * 
     * @param text 
     * @param g
     */
    public static Dimension getTextSize(String text, Graphics g) {
        // get metrics from the graphics
        FontMetrics metrics = g.getFontMetrics(g.getFont());
        // get the height of a line of text in this font and render context
        int hgt = 1;
        if (metrics!=null)
            hgt = metrics.getHeight();
        
        int adv = 1;
        // get the advance of my text in this font and render context
        if (metrics!=null)
            adv = metrics.stringWidth(text);

        // calculate the size of a box to hold the text
        return new Dimension(adv, hgt);
    }

    /**
     * Calculates and returns sum of first n elements from specified table
     *
     * @param tab table with values to sum
     * @param n number of values that will be summed up
     * @return sum of first n values from tab
     */
    public static int getSumOfNElements(int[] tab, int n) {
        if (tab==null || tab.length==0)
            return 0;
        
        if (n>tab.length) n = tab.length;
        
        int sum = 0;
        for (int i=0; i<n; i++)
            sum += tab[i];
        return sum;
    }
    
    /**
     * Inserts new value int table with extending table.
     *
     * @param value Integer value to be insterted into table
     * @param tab Table into which value will be insterted
     * @param index Index - where to insert new value (0 - begin, tab.length - end of the table)
     * @return New extended table with value insterted into it
     */
    public static int[] insertValueAt(int value, int[] tab, int index) {
        int[] newTab    = new int[tab.length+1];

        for(int i=0, n=0; i<tab.length; i++, n++) {
            if (i!=index)   newTab[n]   = tab[i];
            else {
                newTab[n++] = value;
                newTab[n]   = tab[i];
            }
        }

        if (index == tab.length)
            newTab[index] = value;

        return newTab;
    }

    /**
     * Inserts new value int table with extending table.
     *
     * @param value Double value to be insterted into table
     * @param tab Table into which value will be insterted
     * @param index Index - where to insert new value (0 - begin, tab.length - end of the table)
     * @return New extended table with value insterted into it
     */
    public static double[] insertValueAt(double value, double[] tab, int index) {
        double[] newTab    = new double[tab.length+1];

        for(int i=0, n=0; i<tab.length; i++, n++) {
            if (i!=index)   newTab[n]   = tab[i];
            else {
                newTab[n++] = value;
                newTab[n]   = tab[i];
            }
        }

        if (index == tab.length)
            newTab[index] = value;

        return newTab;
    }

    /**
     * Removes value from tab at specified index.
     * Values at other indexs than specified index are copied into new table.
     *
     * @param index Index of element to be removed
     * @param tab Table from wich element is removed
     * @return New table one element shorter, or null if start table was empty or
     *         had single element.
     */
    public static int[] removeValueAt(int index, int[] tab) {
        if (tab==null || tab.length <=1 ) return null;

        int[] newTab = new int[tab.length-1];
        for(int i=0, n=0; i<tab.length; i++)
            if (i!=index)
                newTab[n++] = tab[i];

        return newTab;
    }

    /**
     * Removes value from tab at specified index.
     * Values at other indexs than specified index are copied into new table.
     *
     * @param index Index of element to be removed
     * @param tab Table from wich element is removed
     * @return New table one element shorter, or null if start table was empty or
     *         had single element.
     */
    public static double[] removeValueAt(int index, double[] tab) {
        if (tab==null || tab.length <=1 ) return null;

        double[] newTab = new double[tab.length-1];
        for(int i=0, n=0; i<tab.length; i++)
            if (i!=index)
                newTab[n++] = tab[i];

        return newTab;
    }

    /**
     * Calculates distance between two points.
     *
     */
    public static double getDistance(Point p1, Point p2) {
        return Math.sqrt( (p2.x-p1.x)*(p2.x-p1.x) + (p2.y-p1.y)*(p2.y-p1.y) );
    }

    public static Point toLocalSystem(MyGuiDnDReady c, Point p) {
        return MyGuiHelper.sub(p, c.getDnDLocationOnScreen());
    }

    public static Point sub(Point a, Point b) {
        return new Point((int)(a.getX()-b.getX()), (int)(a.getY()-b.getY()));
    }
    
    public static Point add(Point a, Point b) {
        return new Point((int)(a.getX()+b.getX()), (int)(a.getY()+b.getY()));
    }

    /**
     * Looks for component which is ancestor of both a and b.
     * If a and b don't share any ancestor null is returned.
     * @param a
     * @param b
     * @return Shared ancestor of a and b components.
     */
    public static Component getSharedAncestor(Component a, Component b) {
        Component iterA = a.getParent();
        while(iterA != null) {
            if (MyGuiHelper.isAncestorOf(iterA, b))
                return iterA;
            iterA = iterA.getParent();
        }

        return null;
    }
    /**
     * Finds ancestor of comp which is also child of anc.
     * If there is no such component - null is returned.
     * @param ancestor
     * @param comp
     * @return
     */
    public static Component getGrandChildOf(Container anc, Component comp) {
        Component iter = comp;
        
        while (iter != null) {
            if ( iter.getParent()!=null && iter.getParent().equals(anc))
                return iter;
            iter = iter.getParent();
        }
        
        return null;
    }

    /**
     * Tests if a is ancesor of b.
     * @param a
     * @param b
     * @return
     */
    public static boolean isAncestorOf(Component a, Component b) {
        Component iter = b.getParent();

        while (iter!=null) {
            if (a.equals(iter))
                return true;
            iter = iter.getParent();
        }

        return false;
    }
    /**
     * Compares two components by level on which they lie in a tree of components.
     * @param a
     * @param b
     * @return  1 if a is higher in hierarchy than b,
     *          0 if a's level in equal as b's,
     *         -1 if a is lower in hierarchy than b.
     */
    public static int compareCompsByZIndex(Component a, Component b) {
        int sign = -1;
        if (b==null || !b.isShowing())
            return -1 *sign;
        else if (!a.isShowing())
            return 1 *sign;
        else if (MyGuiHelper.isAncestorOf(a, b))
            return 1 *sign;
        else if (MyGuiHelper.isAncestorOf(b, a))
            return -1 *sign;
        else if (a.equals(b)) return 0;
        else {
            Component ancComp = MyGuiHelper.getSharedAncestor(a, b);
            Container anc = (Container) ancComp;
            Component funnyAncA = getGrandChildOf(anc, a);
            Component funnyAncB = getGrandChildOf(anc, b);
            if (anc==null) {
                return 0;
            } else {
                int zA = anc.getComponentZOrder(funnyAncA),
                    zB = anc.getComponentZOrder(funnyAncB);

                if (zA-zB>0) return -1 *sign;
                else if (zA-zB==0) return 0;
                else if (zA-zB<0) return 1 *sign;
            }
        }
//        System.out.println("compareCompsByZIndex() wasssuuuuppp");
        return 0;
        //throw new Exception("This shuld not happen!");
    }

    public static void printAncestorsOf(Component c) {
        Component iter = c.getParent();
        int count = 0;
        while(iter != null) {
            System.out.println(count+" ancestor of c is "+iter);
            iter = iter.getParent();
        }
    }

    /**
     * Finds out what tabs pane class is most suitable for specified component.
     * 
     * @param tab Component that tabs pnae we look for
     * @return TabsPane object suitable for specified component.
     */
    public static TabsPane getNewTabsPaneForTab(Component tab) {
        Component comp = MyGuiHelper.getComponentAfterScrollPane((JComponent)tab);
        if (comp instanceof CenterTabsPaneTab)       return new CenterTabsPane("");
        else if (comp instanceof UtilTabsPaneTab)    return new UtilTabsPane("");
        //else                                         return new TabsPane("");
        
        return null;
    }


    public static void clearTabsPaneAfterLastTabRemoval(Component tabsPane) {
        if (tabsPane instanceof TabsPane &&
            tabsPane.getParent() instanceof MultiSplitPaneCell &&
            ((TabsPane)tabsPane).isEmpty())
        {
            MultiSplitPaneCell lastParentCell =  (MultiSplitPaneCell) tabsPane.getParent();
            MultiSplitPane lastParentMSP = lastParentCell.getMultiSplitPane();
            lastParentMSP.removeCell( lastParentMSP.getCellIndex(lastParentCell));
            lastParentMSP.componentResized(null);
        }
    }

    /**
     * Iterates up in ancestors hierarchy to find first parent after optional JScrollPane.
     * If comp is not enclosed in JScrollPane, it's standard parent is returned.
     * @param comp
     * @return
     */
    public static Component getFirstAncestorAfterJScrollPane(Component comp) {
        Component iter = comp.getParent();

        while(iter!=null) {
            if (iter instanceof JScrollPane)
                return iter.getParent();
            iter = iter.getParent();
        }

        return comp.getParent();
    }

    public static Component getComponentAfterScrollPane(JComponent comp) {
        if (comp instanceof JScrollPane) {
            JScrollPane sp = (JScrollPane) comp;
            return sp.getViewport().getComponent(0);
        }
        else return comp;
    }
    public static JScrollPane getFirstScrollPaneAncestor(Component comp) {
        while (!(comp instanceof JScrollPane) && comp != null)
            comp = comp.getParent();

        if (comp instanceof JScrollPane)
            return (JScrollPane) comp;
        else
            return null;
    }

    public static String appendSpaces(int count, String dst) {
        while (count-->0)
            dst = " "+dst;

        return new String(dst);
    }

    /**
     * Recursively adds specified focus listener to comp and all its descendants.
     *
     * @param comp Component to which and its children add focus listener
     * @param listener focus listener to be added to 'family' of comp
     */
    public static void sowFocusListenerOnComp(Component comp, FocusListener listener) {
        // Add listener to specified component
        comp.addFocusListener(listener);

        // Add focus listener to all children of specified component
        if (comp instanceof Container)
            for(Component c : ((Container) comp).getComponents())
                MyGuiHelper.sowFocusListenerOnComp(c, listener);
    }

    /**
     * Recursively removes specified focus listener to comp and all its descendants.
     *
     * @param comp Component from which and its children remove focus listener
     * @param listener focus listener to be removed from 'family' of comp
     */
    public static void cropFocusListenerFromComp(Component comp, FocusListener listener) {
        // Add listener to specified component
        comp.removeFocusListener(listener);

        // Add focus listener to all children of specified component
        if (comp instanceof Container)
            for(Component c : ((Container) comp).getComponents())
                MyGuiHelper.cropFocusListenerFromComp(c, listener);
    }

    /**
     * Iterates reqursively through MyGui elements hierarchy and builds list of
     * components of specified class.
     *
     * @param rootMSP Root multi split pane component that holds all other my gui elements.
     * @param c Class object of wchich components we look for.
     * @return ArrayList of found specified class components, if no suach component(s) is present in rootMSP hierarchy
     *         empty list is returned.
     */
    public static ArrayList<Component> listGUIComponentsByClass(MultiSplitPane rootMSP, Class c) {

        ArrayList<Component> list = new ArrayList<Component>();

        for(MultiSplitPaneCell cell : rootMSP.getCells()) {
            Component comp = cell.getComponent(0);

            if (c.isInstance(comp))
                list.add(comp);
            else if (comp instanceof MultiSplitPane) {
                ArrayList<Component> subList = listGUIComponentsByClass((MultiSplitPane) comp, c);
                if (!subList.isEmpty())
                    list.addAll(subList);
            }
        }

        return list;
    }

    public static TabsPane getTabsPaneOf(Component comp, MultiSplitPane rootMSP) {
        ArrayList<Component> list = listGUIComponentsByClass(rootMSP, TabsPane.class);

        for(Component tabsPane : list) {
            TabsPane tp = (TabsPane) tabsPane;
            if (tp.contains(comp))
                return tp;
        }

        return null;
    }
}

